def vulnerable = { def books = Book.find("from Book as b where b.title ='" + params.title + "'") }
def safe = {
def books = Book.find("from Book as b where b.title =?", [params.title])
}
确保所有呈现到视图的数据值都被转义过也是非常重要的。例如当呈现HTML文件或XHTML文件时,你必须对每个对象调用encodeAsHTML,以便保证用户不会向其他人读取的数据和标签恶意注入JavaScript代码或其他HTML代码。Grails为此目的提供了若干个动态编码方法,因此假如你的输出转义格式没有现成的,你可以很容易得编写自己的编码器。
你也必须避免使用请求参数和数据域来决定用户转向的下一个链接。假如你使用一个successURL参数,在你成功登入之后,用来指示用户的转向;这时攻击者可以通过你的网站模拟登入程序,然后一旦登入就把用户转向到他们的网站,这样就潜在允许JS代码使用该网站的登入帐号。
def safeMax = Math.max(params.max?.toInteger(), 100) // never let more than 100 results be returned return Book.list(max:safeMax)
grails-app/utils/目录加载
编解码器。
Grails框架将在 grails-app/utils/目录下查找以Codec结尾命名的类名。例如Grails捆绑的其中一个标准编解码器就是HTMLCodec。
假如一个编解码器包含一个encode属性,该属性被赋予一个代码块,Grails会创建一个动态的encode方法,并把该方法添加到Object类,方法名表示了定义
encode闭包的编解码器。
例如,HTMLCodec类定义了一个编码器代码块,因此Grails会把该闭包与名为encodeAsHTML的Object类相关联。
HTMLCodec类和URLCodec类也定义了解码块,所以Grails会把这些闭包与decodeHTML和decodeURL相关联的。动态编解码器能在Grails应用的任何一个地方执行。例如,考虑一下这种情况,一个报告文件含有一个叫description的属性,
该属性包含了需要被转义显示在HTML文档的特殊字符。GSP文档里,一种处理方法就是用如下的动态编码器编码description属性:
${report.description.encodeAsHTML()}
value.decodeHTML()语句。
编解码器
编解码器执行HTML转义过程和反转义过程,
所以你提供的数值在没有创建任何HTML标签或破坏页面布局下可以被安全得显示出来。例如,给个"Don't
you know that 2 >
1?"字符串,你就不能在HTML页面中安全得显示出来,因为大于符号>看起来像要关闭一个标签,特别是你在某个属性内显示这个字符串,情况会更糟糕,像输入框的value属性
。
使用例子如下:
<input name="comment.message" value="${comment.message.encodeAsHTML()}"/>
注意HTML编码不会重新编码单引号或双引号,你必须对属性值只用两个重复引号避免含有引号的正文毁坏你的页面。URLCodec 当在生成跳转链接,形体处理(form actions)链接,或者任何时候需要数据生成链接时,URL编码是必需的。URL编码可以阻止非法字符串进入链接改变它跳转的目的地,例如"Apple & Blackberry"不能作为get请求中的一个参数,因为&符号为破坏参数解析过程。 使用例子如下:
<a href="/mycontroller/find?searchKey=${lastSearch.encodeAsURL()}">Repeat last search</a>
Your registration code is: ${user.registrationCode.encodeAsBase64()}
Element.update('${elementId}', '${render(template: "/common/message").encodeAsJavaScript()}')
Selected colour: #${[255,127,255].encodeAsHex()}
Your API Key: ${user.uniqueID.encodeAsMD5()}
byte[] passwordHash = params.password.encodeAsMD5Bytes()
Your API Key: ${user.uniqueID.encodeAsSHA1()}
byte[] passwordHash = params.password.encodeAsSHA1Bytes()
Your API Key: ${user.uniqueID.encodeAsSHA256()}
byte[] passwordHash = params.password.encodeAsSHA256Bytes()
许多应用可能定制属于自己的编解码器,Grails在装载标准编解码器时把它们一起装载。定制编解码器类必须在grails-app/utils/目录下定义,而且类名必须以Codec结尾。定制编解码器可能含有一个静态encode块,一个静态decode块或两者皆有。这些编解码代码块需要一个单一参数,当作动态方法操作对象,如下:
class PigLatinCodec { static encode = { str -> // convert the string to piglatin and return the result } }
${lastName.encodeAsPigLatin()}
grails-app/conf/SecurityFilters.groovy类中创建一组新过滤器如下:
class SecurityFilters { def filters = { loginCheck(controller:'*', action:'*') { before = { if(!session.user && actionName != "login") { redirect(controller:"user",action:"login") return false } } } } }
def login = { if(request.get) render(view:"login") else { def u = User.findByLogin(params.login) if(u) { if(u.password == params.password) { session.user = u redirect(action:"home") } else { render(view:"login", model:[message:"Password incorrect"]) } } else { render(view:"login", model:[message:"User not found"]) } } }
accessControl代码块。例子如下:
class ExampleController extends JsecAuthBase { static accessControl = { // All actions require the 'Observer' role. role(name: 'Observer') // The 'edit' action requires the 'Administrator' role. role(name: 'Administrator', action: 'edit') // Alternatively, several actions can be specified. role(name: 'Administrator', only: [ 'create', 'edit', 'save', 'update' ]) } … }